package cn.wen.yaling.xo.service.impl;

import cn.wen.yaling.commons.constant.RedisConstants;
import cn.wen.yaling.commons.dto.UserDTO;
import cn.wen.yaling.commons.entity.UserEntity;
import cn.wen.yaling.commons.enums.ArticleStatus;
import cn.wen.yaling.commons.enums.TypeSort;
import cn.wen.yaling.commons.service.RedisService;
import cn.wen.yaling.commons.utils.*;
import cn.wen.yaling.commons.vo.*;
import cn.wen.yaling.xo.dao.TypeDao;
import cn.wen.yaling.xo.service.CommentService;
import cn.wen.yaling.xo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.ListUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.wen.yaling.xo.dao.ArticleDao;
import cn.wen.yaling.commons.entity.ArticleEntity;
import cn.wen.yaling.xo.service.ArticleService;
import org.springframework.transaction.annotation.Transactional;


@Slf4j
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl<ArticleDao, ArticleEntity> implements ArticleService {

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private CommentService commentService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private TypeDao typeDao;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<ArticleEntity> page = this.page(
                new Query<ArticleEntity>().getPage(params),
                new QueryWrapper<ArticleEntity>()
        );
        return new PageUtils(page);
    }

    @Override
    public List<ArticleListVO> articleListByPage() {
        // 这里的评论数量不能获取 因为获取评论数量就会不实时
        List<ArticleListVO> articleVOS;
        // 1、先判断redis缓存中是否能命中缓存
        if (redisService.hasKey(RedisConstants.CACHE_INDEX_ARTICLE_LIST)) {
            articleVOS = redisUtils.getListCache(RedisConstants.CACHE_INDEX_ARTICLE_LIST, ArticleListVO.class);
        } else {
            // 2、缓存不命中则到db中获取
            articleVOS = articleDao.getArticleList();
            // 3、将db中的缓存获取到
            redisUtils.setListCache(RedisConstants.CACHE_INDEX_ARTICLE_LIST, articleVOS);
        }
        for (ArticleListVO articleVO : articleVOS) {
            articleVO.setCommentNum(commentService.getCommentNumByArticleId(articleVO.getArticleId()));
        }
        return articleVOS;
    }

    @Override
    public List<ArticleVO> getAllArticleList() {
        List<ArticleVO> articleList;
        UserDTO user = UserHolder.getUser();
        String userId = user.getUserId();
        // 1、先判断redis缓存中是否能命中缓存
        if (redisService.hasKey(RedisConstants.CACHE_ADMIN_ARTICLE_LIST + ":" + userId)) {
            articleList = redisUtils.getListCache(RedisConstants.CACHE_ADMIN_ARTICLE_LIST+ ":" + userId, ArticleVO.class);
        } else {
            // 2、缓存不命中则到db中获取
            articleList = articleDao.getArticleListByNoStatus(ArticleStatus.DELETED.getStatus(), userId);
            // 3、将db中的缓存获取到
            redisUtils.setListCache(RedisConstants.CACHE_ADMIN_ARTICLE_LIST+ ":" + userId, articleList);
        }
        return articleList;
    }

    @Override
    public List<ArticleSimpleVO> getArticleRecommendList() {
        // 这里是通过推荐表来实现 所以这里可以添加一个缓存
        List<ArticleSimpleVO> articleEntityList;
        // 1、先判断redis缓存中是否能命中缓存
        if (redisService.hasKey(RedisConstants.CACHE_RECOMMEND_LIST)) {
            articleEntityList = redisUtils.getListCache(RedisConstants.CACHE_RECOMMEND_LIST, ArticleSimpleVO.class);
        } else {
            // 2、缓存不命中则到db中获取
            articleEntityList = articleDao.getArticleRecommendList();
            // 3、将db中的缓存获取到
            redisUtils.setListCache(RedisConstants.CACHE_RECOMMEND_LIST, articleEntityList);
        }
        return articleEntityList;
    }

    @Override
    public List<ArticleSimpleVO> getArticleHottestList() {
        List<ArticleSimpleVO> articleHottestList;
        // 1、先判断redis缓存中是否能命中缓存
        if (redisService.hasKey(RedisConstants.CACHE_HOTTEST_LIST)) {
            articleHottestList = redisUtils.getListCache(RedisConstants.CACHE_HOTTEST_LIST, ArticleSimpleVO.class);
        } else {
            // 2、缓存不命中则到db中获取
            articleHottestList = articleDao.getArticleHottestList();
            // 3、将db中的缓存获取到
            redisUtils.setListCache(RedisConstants.CACHE_HOTTEST_LIST, articleHottestList);
        }
        return articleHottestList;
    }

    @Override
    public List<ArticleSimpleVO> getArticleNewLimitNum(Integer limit) {
        return articleDao.getArticleNewLimitNum(limit);
    }

    @Override
    public Long getArticleCountByStatus(int status) {
        QueryWrapper<ArticleEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("article_status", status);
        queryWrapper.eq("is_publish", 1);
        return articleDao.selectCount(queryWrapper);
    }

    @Override
    public ArticleVO getArticleInfoById(Integer articleId) {
        return articleDao.getArticleInfoById(articleId);
    }

    @Override
    public List<ArticleVO> getArticleListByTypeId(Integer typeId) {
        return articleDao.getArticleListByTypeId(typeId);
    }

    @Override
    public List<ArticleSimpleVO> getArticleHotListByTag(Integer tagId, Integer limit) {
        return articleDao.getArticleHotListByTag(tagId, limit);
    }

    @Override
    public List<ArticleSimpleVO> getArticleHotListByType(Integer typeId, Integer limit) {
        return articleDao.getArticleHotListByType(typeId, limit);
    }

    @Override
    public List<ArchiveArticleVO> getArchiveArticleList() {
        List<ArchiveArticleVO> articleVOS;
        // 1、先判断redis缓存中是否能命中缓存
        if (redisService.hasKey(RedisConstants.CACHE_ARCHIVE_LIST)) {
            articleVOS = redisUtils.getListCache(RedisConstants.CACHE_ARCHIVE_LIST, ArchiveArticleVO.class);
        } else {
            // 2、缓存不命中则到db中获取
            // 1、先获取所有的年份  如果通过年份获取
            List<String> groupYear = articleDao.findGroupByYear();
            articleVOS = new ArrayList<>();
            for (String year : groupYear) {
                ArchiveArticleVO articleVO = new ArchiveArticleVO();
                articleVO.setGroupYear(year);
                articleVO.setArticles(articleDao.findArticleByYear(year));
                articleVOS.add(articleVO);
            }
            // 3、将db中的缓存获取到
            redisUtils.setListCache(RedisConstants.CACHE_ARCHIVE_LIST, articleVOS);
        }
        return articleVOS;

    }

    @Override
    public void updateArticleViewById(Integer articleViews, Integer articleId) {
        articleDao.updateArticleViewById(articleViews, articleId);
    }

    @Override
    public List<ArticleListVO> getArticleByKeyword(QueryVO queryVO) {
        List<ArticleListVO> articleListVOS = articleDao.getArticleByKeyword(queryVO);
        for (ArticleListVO articleListVO : articleListVOS) {
            articleListVO.setCommentNum(commentService.getCommentNumByArticleId(articleListVO.getArticleId()));
        }
        return articleListVOS;
    }

    @Transactional
    @Override
    public boolean addArticle(ArticleAddVO articleAddVO) {
        // 需要删除缓存
        // 删除 首页缓存
        // 删除 最热缓存
        // 删除 推荐缓存
        redisUtils.delArticleListCache();
        log.info(articleAddVO.toString());
        // 然后再添加到数据库中
        String userId = articleAddVO.getUserId();
        Integer[] tagIds = articleAddVO.getTagIds();
        Integer typeId = articleAddVO.getTypeId();
        UserEntity user = userService.getUserByUserId(userId);
        ArticleEntity target = new ArticleEntity();
        BeanUtils.copyProperties(articleAddVO, target);
        target.setCreateTime(new Date());
        target.setUpdateTime(new Date());
        target.setArticleViews(0);
        target.setArticleAuthor(user.getNickname());
        // 判断是发布还是保存数据
        target.setArticleStatus(target.getIsPublish() ? ArticleStatus.PUBLISHED.getStatus()
                : ArticleStatus.UNPUBLISHED.getStatus());
        articleDao.insert(target);
        articleDao.insertArticleAndUser(userId, target.getArticleId());
        articleDao.insertArticleAndTags(target.getArticleId(), tagIds);
        articleDao.insertArticleAndType(target.getArticleId(), typeId);
        // 通过TypeId判断是否是Subject
        Integer subjectId = typeDao.selectSubjectIdByTypeId(articleAddVO.getTypeId());
        if (subjectId != null) {
            redisUtils.delSubjectArticleListCache(subjectId);
        }
        return true;
    }

    /**
     * 需要修改的文章信息
     *
     * @param article
     * @return
     */
    @Transactional
    @Override
    public boolean updateArticle(ArticleUpdateVO article) {
        boolean updateRes = false;
        try {
            // 修改流程
            // 1、判断是否需要修改typeId 内容
            Integer articleId = article.getArticleId();
            // 先删除分类 再添加分类
            articleDao.deleteArticleTypeRelationByArticleId(articleId);
            // 添加到分类关系中
            articleDao.insertArticleAndType(articleId, article.getTypeId());
            // 2、是否需要修改 tagId 内容
            List<Integer> tagIds = articleDao.getArticleTagsByArticleId(articleId);
            // 需要判断是否需要更改
            boolean isEqual = ListUtils.isEqualList(Arrays.asList(article.getTagIds()), tagIds);
            if (!isEqual) {
                // 不相等则需要更改
                // 先删除全部的标签再添加
                // 判断是否需要删除
                if (tagIds.size() != 0) {
                    boolean resDelete = articleDao.deleteTagsById(articleId);
                    if (!resDelete) throw new Exception("修改标签失败！！");
                }
                // 添加
                articleDao.insertArticleAndTags(articleId, article.getTagIds());
            }
            // 3、最后修改文章的基本信息
            ArticleEntity target = new ArticleEntity();
            BeanUtils.copyProperties(article, target);
            target.setUpdateTime(new Date());
            updateRes = articleDao.updateById(target) > 0;
            if (!updateRes) throw new Exception("修改文章失败！");
            // 4、删除文章的缓存
            redisUtils.delArticleListCache();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return updateRes;
    }

    @Override
    public boolean updateArticlePublishStatus(Integer articleId, Boolean isPublish) {
        // 需要修改数据库再删除缓存
        boolean res = articleDao.updateArticlePublishStatusById(articleId, isPublish, isPublish ?
                ArticleStatus.PUBLISHED.getStatus() : ArticleStatus.UNPUBLISHED.getStatus());
        if (res) {
            // 删除缓存
            redisUtils.delArticleListCache();
        }
        return res;
    }

    @Override
    public boolean updateArticleDeleteStatus(Integer[] articleIds) {
        boolean res = articleDao.updateByIds(articleIds, ArticleStatus.DELETED.getStatus());
        if (res) {
            // 删除缓存
            redisUtils.delArticleListCache();
        }
        return res;
    }

    @Override
    public Map<String, Object> getBlogContributeCount() {
        // 获取今天结束时间
        String endTime = DateUtil.getNowTime();
        // 获取365天前的日期
        Date temp = DateUtil.getDate(endTime, -365);
        String startTime = DateUtil.dateTimeToStr(temp);
        List<ArticleTypeVO> blogContributeMap = articleDao.getBlogContributeCount(startTime, endTime);
        List<String> dateList = DateUtil.getDayBetweenDates(startTime, endTime);
        Map<String, Object> dateMap = new HashMap<>();
        for (ArticleTypeVO item : blogContributeMap) {
            dateMap.put(item.getDate(), item.getCount());
        }
        List<List<Object>> resultList = new ArrayList<>();
        for (String item : dateList) {
            Integer count = 0;
            if (dateMap.get(item) != null) {
                count = Integer.valueOf(dateMap.get(item).toString());
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(item);
            objectList.add(count);
            resultList.add(objectList);
        }
        Map<String, Object> resultMap = new HashMap<>(2);
        List<String> contributeDateList = new ArrayList<>();
        contributeDateList.add(startTime);
        contributeDateList.add(endTime);
        resultMap.put("contributeDate", contributeDateList);
        resultMap.put("blogContributeCount", resultList);
        return resultMap;
    }

    @Override
    public List<ArticleVO> getArticleListBySearch(ArticleQueryVO articleQueryVO) {
        return articleDao.getArticleListSearch(articleQueryVO, UserHolder.getUser().getUserId());
    }

    @Transactional
    @Override
    public ResultEntity insertArticleToSubject(Integer[] articleIds, Integer subjectId) {
        // 1.获取所有的文章列表 需要判断是否在专栏的 分类中
        List<Integer> typeList = articleDao.selectTypeListByArticleId(articleIds,
                TypeSort.SUBJECT_TYPE.getStatus());
        // 2.通过专题Id 和 分类名称判断是否匹配
        List<Integer> types = articleDao.getTypeListBySubjectId(subjectId);
        // 3.判断typeList 是否为 types 的子集
        boolean res = types.containsAll(typeList);
        if (!res) return ResultEntity.error("存在文章分类不属于该专题分类！");
        // 4.需要判断有没有重复
        boolean checkRes = articleDao.checkArticleInSubject(articleIds);
        if (checkRes) return ResultEntity.error("存在重复的文章！");
        // 5.添加到该表中
        boolean isInsert = articleDao.updateArticleIdsAndSubjectId(articleIds);
        if (!isInsert) return ResultEntity.error();
        // 删除subjectId相关的文章
        redisUtils.delSubjectArticleListCache(subjectId);
        return ResultEntity.ok();
    }

    @Override
    public ResultEntity deleteArticleFromSubjectById(Integer articleId, Integer subjectId) {
        // 1.判断是否存在当前记录
        boolean isExist = articleDao.checkArticleFromSubject(articleId, subjectId);
        if (!isExist) return ResultEntity.error("移除失败！");
        boolean isDelete = articleDao.deleteArticleIdAndSubjectId(articleId, subjectId);
        if (!isDelete) return ResultEntity.error();
        // 删除subjectId相关的文章
        redisUtils.delSubjectArticleListCache(subjectId);
        return ResultEntity.ok();
    }

    @Override
    public ResultEntity updateArticleRecommend(List<ArticleRecommendVO> recommendList) {
        // 因为recommendId是主键索引 所有使用主键索引来更改数据效率高
        boolean isUpdate = articleDao.updateArticleRecommend(recommendList);
        if (isUpdate) return ResultEntity.ok();
        return ResultEntity.error();
    }

    @Override
    public List<ArticleAdminVO> getSimpleArticleListByTypeId(Integer typeId) {
        return articleDao.getSimpleArticleListByTypeId(typeId);
    }

    @Override
    public ResultEntity updateArticlePriority(List<ArticlePriorityVO> articlePriorityList) {
        // 批量修改文章信息
        List<ArticleEntity> targetList = new ArrayList<>();
        articlePriorityList.forEach((item) -> {
            ArticleEntity target = new ArticleEntity();
            target.setArticleId(item.getArticleId());
            target.setArticlePriority(item.getGrade());
            targetList.add(target);
        });
        boolean res = this.updateBatchById(targetList);
        if (!res) return ResultEntity.error();
        // 删除缓存
        redisUtils.delSubjectArticleListCache(articlePriorityList.get(0).subjectId);
        return ResultEntity.ok();
    }

    @Override
    public boolean updateArticleCommentStatus(Integer articleId, Boolean status) {
        // 需要修改数据库再删除缓存
        ArticleEntity target = new ArticleEntity();
        target.setArticleId(articleId);
        target.setOpenComment(status);
        boolean res = articleDao.updateById(target) > 0;
        if (res) {
            // 删除缓存
            redisUtils.delArticleListCache();
        }
        return res;

    }

    @Override
    public boolean updateArticleAppreciationStatus(Integer articleId, Boolean status) {
        // 需要修改数据库再删除缓存
        ArticleEntity target = new ArticleEntity();
        target.setArticleId(articleId);
        target.setIsAppreciation(status);
        boolean res = articleDao.updateById(target) > 0;
        if (res) {
            // 删除缓存
            redisUtils.delArticleListCache();
        }
        return res;
    }

    @Override
    public List<RecommendArticleVO> getRecommendListByLevel(Integer level) {
        return articleDao.getRecommendListByLevel(level);
    }

    @Override
    public ResultEntity updateBatchRecommendArticle(List<RecommendArticleVO> list) {
        List<ArticleEntity> targetList = new ArrayList<>();
        list.forEach((item) -> {
            ArticleEntity target = new ArticleEntity();
            target.setArticleId(item.getArticleId());
            target.setRecommendPriority(item.getRecommendPriority());
            targetList.add(target);
        });
        boolean res = this.updateBatchById(targetList);
        if (res) return ResultEntity.ok();
        return ResultEntity.error();
    }

}